home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK2.toast / Development Kits (Disc 2) / QuickDraw GX / Programming Stuff / GX Libraries / LayoutLibrary.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-23  |  16.7 KB  |  520 lines  |  [TEXT/MPS ]

  1.  
  2. /*
  3.     File:        LayoutLibrary.c
  4.  
  5.     Contains:    Line Layout library routines.
  6.  
  7.     Written by:    Dave Opstad, Eric Mader
  8.     
  9.     Copyright:    ©1989-1995 by Apple Computer, Inc., all rights reserved.
  10.  
  11.     Change History (most recent first):
  12.  
  13.          <3>     6/23/95    DGO        Reorganized and cleaned up, including adding comments for every
  14.                                     routine.
  15.          <2>      1/9/95    JD        changed 'boolean' to 'Boolean'
  16.          <1>      1/9/95    JD        First checked in (for earlier history, see skia project file).
  17.  
  18. */
  19.  
  20. #include <Types.h>
  21. #include <Memory.h>
  22. #include <Resources.h>
  23. #include <ToolUtils.h>
  24.  
  25. #include <GXGraphics.h>
  26. #include <GXMath.h>
  27. #include <GXTypes.h>
  28. #include <GXLayout.h>
  29. #include <GXFonts.h>
  30.  
  31. #include "LayoutLibrary.h"
  32. #include "FontLibrary.h"
  33.  
  34. /* -------------------------------------------------------------------------------------------- */
  35.  
  36. /* CONSTANTS */
  37.  
  38. enum
  39.     {
  40.     extraLineGap        = ff(2),
  41.     lineStartsCount        = 50
  42.     };
  43.  
  44. /* -------------------------------------------------------------------------------------------- */
  45.  
  46. /* INTERNAL PROTOTYPES */
  47.  
  48. static short CountBytes(char *text);
  49. static long GetNextOffset(gxShape layout, long offset);
  50. static long GetPreviousOffset(gxShape layout, long offset);
  51. static char *GetTextPiecePtr(const void *text[], const short textRunLengths[], short offset);
  52. static void SetStyleNamedFont(gxStyle s, unsigned char *name);
  53.  
  54. /* -------------------------------------------------------------------------------------------- */
  55.  
  56. /* INTERNAL ROUTINES */
  57.  
  58. /* CountBytes is used by NewParagraph. It finds the string length up to but not including
  59.     the first CR or HT. */
  60.  
  61. static short CountBytes(char *text)
  62.     {
  63.     short count = 0;
  64.     
  65.     while (*text != 13 && *text != 9)
  66.         {
  67.         text += 1;
  68.         count += 1;
  69.         }
  70.     
  71.     return count;
  72.     } /* CountBytes */
  73.  
  74. /* -------------------------------------------------------------------------------------------- */
  75.  
  76. /* GetNextOffset returns the offset of the next character in the shape, taking into account
  77.     8-bit and 16-bit characters and ligatures. */
  78.  
  79. static long GetNextOffset(gxShape layout, long offset)
  80.     {
  81.     gxLayoutOffsetState    offsetState;
  82.     static long            offsetStateSizes[] = {1, 2, 1, 2, 0};
  83.     unsigned short        firstGlyph, secondGlyph;
  84.     
  85.     GXGetOffsetGlyphs(layout, offset, 0, &offsetState, &firstGlyph, &secondGlyph);
  86.     
  87.     return offset + offsetStateSizes[offsetState & ~gxOffsetInsideLigature];
  88.     }    /* GetNextOffset */
  89.  
  90. /* -------------------------------------------------------------------------------------------- */
  91.  
  92. /* GetPreviousOffset returns the offset of the prior character in the shape, taking into
  93.     account 8-bit and 16-bit characters and ligatures. */
  94.  
  95. static long GetPreviousOffset(gxShape layout, long offset)
  96.     {
  97.     gxLayoutOffsetState    offsetState;
  98.     static long            offsetStateSizes[] = {1, 1, 2, 2, 0};
  99.     unsigned short        firstGlyph, secondGlyph;
  100.     
  101.     GXGetOffsetGlyphs(layout, offset, 0, &offsetState, &firstGlyph, &secondGlyph);
  102.     
  103.     return offset - offsetStateSizes[offsetState & ~gxOffsetInsideLigature];
  104.     }    /* GetPreviousOffset */
  105.  
  106. /* -------------------------------------------------------------------------------------------- */
  107.  
  108. /* GetTestPiecePtr returns a chunk of text from the (possibly disparate) individual text
  109.     pieces. It is used by NewStyledParagraph. */
  110.  
  111. static char *GetTextPiecePtr(const void *text[], const short textRunLengths[], short offset)
  112.     {
  113.     char    **pPiece = (char **) text;
  114.     short    oCopy = offset, *runLength = (short *) textRunLengths;
  115.     
  116.     while (oCopy > *runLength)
  117.         {
  118.         oCopy -= *runLength++;
  119.         pPiece += 1;
  120.         }
  121.     
  122.     return *pPiece + oCopy;
  123.     } /* GetTextPiecePtr */
  124.  
  125. /* -------------------------------------------------------------------------------------------- */
  126.  
  127. /* SetStyleNamedFont sets the specified gxStyle's font to that font with the specified
  128.     full name. Note that name is a Pascal string. */
  129.  
  130. static void SetStyleNamedFont(gxStyle s, unsigned char *name)
  131.     {
  132.     gxFont    fontID = FindPNameFont(gxFullFontName, name);
  133.     
  134.     if (fontID != GXGetStyleFont(s))
  135.         GXSetStyleFont(s, fontID);
  136.     }    /* SetStyleNamedFont */
  137.  
  138. /* -------------------------------------------------------------------------------------------- */
  139.  
  140. /* EXPORTED ROUTINES */
  141.  
  142. /* DisposeParagraph disposes of the memory associated with a paragraph. While currently
  143.     simple, it could be more involved if the paragraph itself retains more information. */
  144.  
  145. void DisposeParagraph(ParagraphRecordHandle paraRec)
  146.     {
  147.     short    i;
  148.     
  149.     for (i = 0; i < (**paraRec).nLayouts; i += 1)
  150.         GXDisposeShape((**paraRec).layouts[i]);
  151.     
  152.     DisposeHandle((Handle) paraRec);
  153.     } /* DisposeParagraph */
  154.  
  155. /* -------------------------------------------------------------------------------------------- */
  156.  
  157. /* GetLayoutBounds returns a gxShape corresponding to the bounds of the specified layout. It
  158.     differs from GXGetShapeBounds in two respects: first, it returns a shape (rather than just
  159.     a rectangle); and second, it takes both the layout's position and its mapping into account
  160.     (GXGetShapeBounds only takes the layout's position into account). */
  161.  
  162. gxShape GetLayoutBounds(gxShape layout)
  163.     {
  164.     gxMapping     thisMapping;
  165.     gxRectangle    thisRect;
  166.     gxShape     rectShape;
  167.     
  168.     GXGetShapeBounds(layout, 0, &thisRect);
  169.     rectShape = GXNewRectangle(&thisRect);
  170.     GXMapShape(rectShape, GXGetTransformMapping(GXGetShapeTransform(layout), &thisMapping));
  171.     return rectShape;
  172.     } /* GetLayoutBounds */
  173.  
  174. /* -------------------------------------------------------------------------------------------- */
  175.  
  176. /* InitializeLayoutOptions sets the default values into a gxLayoutOptions structure. */
  177.  
  178. void InitializeLayoutOptions(gxLayoutOptions *layoutOptions)
  179.     {
  180.     layoutOptions->width = 0;
  181.     layoutOptions->flush = 0;
  182.     layoutOptions->just = 0;
  183.     layoutOptions->flags = 0;
  184.     layoutOptions->baselineRec = NULL;
  185.     }    /* InitializeLayoutOptions */
  186.  
  187. /* -------------------------------------------------------------------------------------------- */
  188.  
  189. /* InitializeRunControls sets all the fields of the specified gxRunControls struct to
  190.     values indicating default behavior. */
  191.  
  192. void InitializeRunControls(gxRunControls *runControls)
  193.     {
  194.     runControls->flags = 0;
  195.     runControls->beforeWithStreamShift = 0;
  196.     runControls->afterWithStreamShift = 0;
  197.     runControls->crossStreamShift = 0;
  198.     runControls->imposedWidth = 0;
  199.     runControls->track = 0;
  200.     runControls->hangingInhibitFactor = 0;
  201.     runControls->kerningInhibitFactor = 0;
  202.     runControls->baselineType = gxRomanBaseline;
  203.     runControls->decompositionAdjustmentFactor = 0;
  204.     }    /* InitializeRunControls */
  205.  
  206. /* -------------------------------------------------------------------------------------------- */
  207.  
  208. /* InitializeStyleRunOverrides initializes all the fields of the specified StyleRunOverrides
  209.     struct to their default values. */
  210.  
  211. void InitializeStyleRunOverrides(StyleRunOverrides *overrides)
  212.     {
  213.     overrides->priorityJustOverride = NULL;
  214.     overrides->glyphJustOverrides = NULL;
  215.     overrides->glyphJustOverridesCount = 0;
  216.     overrides->glyphSubstitutions = NULL;
  217.     overrides->glyphSubstitutionsCount = 0;
  218.     overrides->kerningAdjustments = NULL;
  219.     overrides->kerningAdjustmentsCount = 0;
  220.     }    /* InitializeStyleRunOverrides */
  221.  
  222. /* -------------------------------------------------------------------------------------------- */
  223.  
  224. /* NewLayoutStyle allocated and then initializes a gxStyle with the specified text-related
  225.     attributes. */
  226.  
  227. gxStyle NewLayoutStyle(char *gxFontName, Fixed textSize, gxTextAttribute attr,
  228.     gxRunControls *runControls, gxRunFeature runFeatures[], long runFeaturesCount,
  229.     StyleRunOverrides *overrides)
  230.  
  231.     {
  232.     gxStyle    newStyle = GXNewStyle();
  233.  
  234.     SetLayoutStyle(newStyle, gxFontName, textSize, attr, runControls, runFeatures,
  235.         runFeaturesCount, overrides);
  236.     
  237.     return newStyle;
  238.     }    /* NewLayoutStyle */
  239.  
  240. /* -------------------------------------------------------------------------------------------- */
  241.  
  242. /* NewParagraph is the simplest of the paragraph creation functions. It assumes a single
  243.     gxStyle for the whole paragraph, and does its own deduction about where the paragraph
  244.     ends (namely at a hard stop, CR or HT). */
  245.  
  246. ParagraphRecordHandle NewParagraph(char *text, gxStyle baseStyle, Fixed width, long justified,
  247.     Fixed lineHeight, gxPoint *firstOrigin)
  248.     
  249.     {
  250.     Boolean                    startIsStaked;
  251.     char                    *pChar;
  252.     Fixed                     currLineDelta, textSize;
  253.     gxByteOffset            lineStarts[lineStartsCount], newLineStart, nextStake, nls2, priorStake,
  254.                             thisLineStart;
  255.     gxLayoutOptions         options;
  256.     gxShape                 bigLayout, thisLine;
  257.     ParagraphRecordHandle    paraHandle;
  258.     short                     byteCount, i, nextLineIndex;
  259.     
  260.     byteCount = CountBytes(text); /* doesn't count the CR or HT!! */
  261.     bigLayout = GXNewLayout(1, &byteCount, (const void **) &text, 1, &byteCount, &baseStyle,
  262.         0, NULL, NULL, NULL, firstOrigin);
  263.     
  264.     /* We next compute the line breaks and store the offsets that correspond to them in a
  265.         temporary array. */
  266.     
  267.     thisLineStart = 0;
  268.     nextLineIndex = 0;
  269.     while (thisLineStart < byteCount && nextLineIndex < lineStartsCount - 1)
  270.         {
  271.         lineStarts[nextLineIndex++] = thisLineStart;
  272.         newLineStart = GXGetLayoutBreakOffset(bigLayout, thisLineStart, width, 0, NULL,
  273.             &startIsStaked, &priorStake, &nextStake);
  274.         if (newLineStart == byteCount)
  275.             break;
  276.         /* Now backtrack to first prior space. */
  277.         nls2 = newLineStart;
  278.         pChar = text + newLineStart - 1;
  279.         while (nls2 >= 0 && *pChar != ' ')
  280.             {
  281.             pChar -= 1;
  282.             nls2 -= 1;
  283.             }
  284.         if (nls2 < 0)
  285.             thisLineStart = newLineStart;
  286.         else
  287.             thisLineStart = nls2;
  288.         }
  289.     
  290.     lineStarts[nextLineIndex] = byteCount;
  291.     
  292.     /* Allocate space for the ParagraphRecord. */
  293.     
  294.     paraHandle = (ParagraphRecordHandle) NewHandle((Size) (sizeof(ParagraphRecord) +
  295.         nextLineIndex * sizeof(gxShape)));
  296.     (**paraHandle).nLayouts = nextLineIndex;
  297.     
  298.     /* Now create the layouts and place them in the ParagraphRecord. */
  299.     
  300.     InitializeLayoutOptions(&options);
  301.     options.width = width;
  302.     if (justified)
  303.         options.just = fract1;
  304.     textSize = GXGetStyleTextSize(baseStyle);
  305.     currLineDelta = 0;
  306.     
  307.     for (i = 0; i < nextLineIndex; i++)
  308.         {
  309.         if (i == nextLineIndex - 1)
  310.             options.just = 0; /* don't justify the last line... */
  311.         thisLine = GXNewLayoutFromRange(bigLayout, lineStarts[i], lineStarts[i+1], &options, NULL);
  312.         if (currLineDelta)
  313.             GXMoveShape(thisLine, 0, currLineDelta);
  314.         (**paraHandle).layouts[i] = thisLine;
  315.         currLineDelta += (lineHeight ? lineHeight : (textSize + extraLineGap));
  316.         }
  317.     
  318.     (**paraHandle).totalHeight = currLineDelta;
  319.     
  320.     GXDisposeShape(bigLayout);
  321.     return paraHandle;
  322.     } /* NewParagraph */
  323.  
  324. /* -------------------------------------------------------------------------------------------- */
  325.  
  326. /* NewSingleLayout builds a layout shape with the specified text, and with the specified text-
  327.     related attributes. It is basically a single call that replaces the sequence: allocate a
  328.     style, set up the style, and call GXNewLayout to make the layout. */
  329.  
  330. gxShape NewSingleLayout(char *text, char *gxFontName, Fixed textSize, gxLayoutOptions *options,
  331.     gxPoint *position, gxTextAttribute attr, gxRunControls *runControls,
  332.     gxRunFeature runFeatures[], long runFeaturesCount, StyleRunOverrides *overrides)
  333.  
  334.     {
  335.     char    *s = &text[0];
  336.     gxShape    newLayoutShape;
  337.     gxStyle    newLayoutStyle;
  338.     short    len;
  339.     
  340.     newLayoutStyle = NewLayoutStyle(gxFontName, textSize, attr, runControls, runFeatures,
  341.         runFeaturesCount, overrides);
  342.  
  343.     for (len = 0; *s++ != 0; len += 1)
  344.         ;
  345.     
  346.     newLayoutShape = GXNewLayout(1, &len, (const void **) &text, 1, &len, &newLayoutStyle,
  347.         0, NULL, NULL, options, position);
  348.     
  349.     GXDisposeStyle(newLayoutStyle);
  350.     
  351.     return newLayoutShape;
  352.     }    /* NewSingleLayout */
  353.  
  354. /* -------------------------------------------------------------------------------------------- */
  355.  
  356. /* NewStyledParagraph takes inputs similar to those taken by GXNewLayout, but instead of
  357.     creating a single line, it creates a paragraph of lines. */
  358.  
  359. ParagraphRecordHandle NewStyledParagraph(long textRunCount, const void *text[],
  360.     const short textRunLengths[], long styleRunCount, const gxStyle styles[],
  361.     const short styleRunLengths[], long levelRunCount, const short levels[],
  362.     const short levelRunLengths[], long totalByteCount, const gxLayoutOptions *layoutOptions,
  363.     Fixed lineHeight, const gxPoint *firstOrigin)
  364.     
  365.     {
  366.     Boolean                 startIsStaked;
  367.     gxByteOffset            lineStarts[lineStartsCount], newLineStart, nextStake,
  368.                             nls2, priorStake, thisLineStart;
  369.     char                    *pChar, *pSav;
  370.     Fixed                     currLineDelta, lineAscent, lineDescent;
  371.     gxLayoutOptions         specialOptions;
  372.     ParagraphRecordHandle    paraHandle;
  373.     gxShape                 bigLayout, thisLine;
  374.     short                     i, nextLineIndex;
  375.     
  376.     specialOptions = *layoutOptions;
  377.     specialOptions.just = specialOptions.flush = 0;
  378.     specialOptions.width = 0;
  379.     
  380.     bigLayout = GXNewLayout(textRunCount, textRunLengths, text, styleRunCount, styleRunLengths,
  381.         styles, levelRunCount, levelRunLengths, levels, &specialOptions, firstOrigin);
  382.     
  383.     /* We next compute the gxLine breaks and store the offsets that correspond to them in a
  384.         temporary array. */
  385.     
  386.     thisLineStart = 0;
  387.     nextLineIndex = 0;
  388.     while (thisLineStart < totalByteCount && nextLineIndex < lineStartsCount - 1)
  389.         {
  390.         lineStarts[nextLineIndex++] = thisLineStart;
  391.         newLineStart = GXGetLayoutBreakOffset(bigLayout, thisLineStart, layoutOptions->width, 0,
  392.             NULL, &startIsStaked, &priorStake, &nextStake);
  393.         if (newLineStart == totalByteCount)
  394.             break;
  395.         /* Now backtrack to first prior space. */
  396.         nls2 = newLineStart;
  397.         pSav = pChar = GetTextPiecePtr(text, textRunLengths, GetPreviousOffset(bigLayout,
  398.             newLineStart));
  399.         while (nls2 >= lineStarts[nextLineIndex-1] && *pChar != ' ')
  400.             pChar = GetTextPiecePtr(text, textRunLengths, nls2 = GetPreviousOffset(bigLayout, nls2));
  401.  
  402.         if (nls2 <= lineStarts[nextLineIndex-1])
  403.             thisLineStart = newLineStart;
  404.         else
  405.             {
  406.             if (pSav != pChar)
  407.                 nls2 = GetNextOffset(bigLayout, nls2);
  408.             thisLineStart = nls2;
  409.             }
  410.         }
  411.     lineStarts[nextLineIndex] = (short) totalByteCount;
  412.     
  413.     /* Allocate space for the ParagraphRecord. */
  414.     
  415.     paraHandle = (ParagraphRecordHandle) NewHandle((Size) (sizeof(ParagraphRecord) +
  416.         nextLineIndex * sizeof(gxShape)));
  417.     (**paraHandle).nLayouts = nextLineIndex;
  418.     
  419.     /* Now create the layouts and place them in the ParagraphRecord. */
  420.     
  421.     specialOptions = *layoutOptions;
  422.     currLineDelta = 0;
  423.     
  424.     for (i = 0; i < nextLineIndex; i++)
  425.         {
  426.         if (i == nextLineIndex - 1)
  427.             specialOptions.just = 0;    /* don't justify last line... */
  428.         thisLine = GXNewLayoutFromRange(bigLayout, lineStarts[i], lineStarts[i+1],
  429.             &specialOptions, NULL);
  430.         if (currLineDelta)
  431.             GXMoveShape(thisLine, 0, currLineDelta);
  432.         (**paraHandle).layouts[i] = thisLine;
  433.         if (lineHeight)
  434.             currLineDelta += lineHeight;
  435.         else
  436.             {
  437.             GXGetLayoutSpan(thisLine, &lineAscent, &lineDescent);
  438.             currLineDelta += lineAscent + lineDescent + extraLineGap;
  439.             }
  440.         }
  441.     
  442.     (**paraHandle).totalHeight = currLineDelta;
  443.     
  444.     GXDisposeShape(bigLayout);
  445.     return paraHandle;
  446.     } /* NewStyledParagraph */
  447.  
  448. /* -------------------------------------------------------------------------------------------- */
  449.  
  450. /* SetDefaultPriorityJustOverride initializes the fields of all the priorities in the specified
  451.     gxPriorityJustificationOverride struct to zero. */
  452.  
  453. void SetDefaultPriorityJustOverride(gxPriorityJustificationOverride *override)
  454.     {
  455.     gxWidthDeltaRecord    *pDelta = override->deltas;
  456.     short                i;
  457.  
  458.     for (i = 0; i < gxNumberOfJustificationPriorities; i += 1)
  459.         {
  460.         pDelta->growFlags = 0;
  461.         pDelta->shrinkFlags = 0;
  462.         pDelta->beforeGrowLimit = 0;
  463.         pDelta->afterGrowLimit = 0;
  464.         pDelta->beforeShrinkLimit = 0;
  465.         (pDelta++)->afterShrinkLimit = 0;
  466.         } /* endloop i */
  467.     }    /* SetDefaultPriorityJustOverride */
  468.  
  469. /* -------------------------------------------------------------------------------------------- */
  470.  
  471. /* SetLayoutStyle is a fast way of setting up an existing gxStyle with lots of text-related
  472.     attributes. If you want to allocate a new gxStyle and initialize it at the same time, see
  473.     the NewLayoutStyle routine. */
  474.  
  475. void SetLayoutStyle(gxStyle s, char *gxFontName, Fixed textSize, gxTextAttribute attr,
  476.     gxRunControls *runControls, gxRunFeature runFeatures[], long runFeaturesCount,
  477.     StyleRunOverrides *overrides)
  478.  
  479.     {
  480.     gxRunControls    localControls;
  481.  
  482. #ifdef debugging    
  483.     GXIgnoreGraphicsNotice(attributes_already_set);
  484.     GXIgnoreGraphicsNotice(text_attributes_already_set);
  485. #endif
  486.  
  487.     SetStyleNamedFont(s, (unsigned char *) gxFontName);
  488.     GXSetStyleTextSize(s, textSize);
  489.     GXSetStyleTextAttributes(s, attr);
  490.  
  491.     if (runControls == NULL)
  492.         {
  493.         InitializeRunControls(&localControls);
  494.         runControls = &localControls;
  495.         }
  496.     
  497.     GXSetStyleRunControls(s, runControls);
  498.  
  499.     if (runFeatures != NULL)
  500.         GXSetStyleRunFeatures(s, runFeaturesCount, runFeatures);
  501.  
  502.     if (overrides != NULL)
  503.         {
  504.         if (overrides->glyphSubstitutions != NULL)
  505.             GXSetStyleRunGlyphSubstitutions(s, overrides->glyphSubstitutionsCount, overrides->glyphSubstitutions);
  506.         if (overrides->kerningAdjustments != NULL)
  507.             GXSetStyleRunKerningAdjustments(s, overrides->kerningAdjustmentsCount, overrides->kerningAdjustments);
  508.         if (overrides->glyphJustOverrides != NULL)
  509.             GXSetStyleRunGlyphJustOverrides(s, overrides->glyphJustOverridesCount, overrides->glyphJustOverrides);
  510.         if (overrides->priorityJustOverride != NULL)
  511.             GXSetStyleRunPriorityJustOverride(s, overrides->priorityJustOverride);
  512.         }
  513.  
  514. #ifdef debugging    
  515.     GXPopGraphicsNotice();
  516.     GXPopGraphicsNotice();
  517. #endif
  518.  
  519.     }    /* SetLayoutStyle */
  520.